/* Copyright 2007 Laura Giordano, Valentina Gliozzi, Gian Luca Pozzato This file is part of KLMLean. KLMLean is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. KLMLean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with KLMLean. If not, see . */ /* Theorem prover for KLM Rational logic R - free variables version */ :- op(400,xfy,<), op(400,fy,neg), op(400,fy,box), op(500,xfy,and), op(600,xfy,or), op(650,xfy,->), op(650,xfy,=>). :-use_module(library(lists)). :-use_module(library(clpfd)). /* --------------------------------------------------------------------------------------- */ /* The following predicate implements the calculi: prove(Gamma,1,[],Tree) iff there exists a closed tableaux Tree for Gamma [Y,<,X] is used to represent that Y < X Lables are represented by integers starting from 1 In prove(Gamma,Max,Cond,Tree) we have that: - Max is the last label introduced in the current branch (the maximum integer in the branch) - Cond is a list of pairs [A => B,Used], where Used is a list of free variables, representing that the (|~+) rule has already been applied in the current branch by introducing the labels in Used. In order to ensure termination, (|~+) is applied to A=> B only if the cardinality of Used, i.e. the number of free variables already used to apply the rule to A => B in the current branch, is < than Max. The predicate all_different is used in order to ensure that all free variables in Used assume different values (it is useless to apply (|~+) more than once in the same world in each branch of a proof tree) */ prove(Gamma,_,_,tree(axiom)):-member([X,F],Gamma),member([X,neg F],Gamma),!. prove(Gamma,_,_,tree(axiom)):-member([X,<,Y],Gamma),member([Y,<,X],Gamma),!. prove(Gamma,Max,Cond,tree(neg,[X,neg neg A],SubTree)):- select([X,neg neg A],Gamma,NewGamma),!, prove([[X,A]|NewGamma],Max,Cond,SubTree). prove(Gamma,Max,Cond,tree(andP,[X,A and B],SubTree)):- select([X,A and B],Gamma,NewGamma),!, prove([[X,A]|[[X,B]|NewGamma]],Max,Cond,SubTree). prove(Gamma,Max,Cond,tree(impN,[X,neg (A -> B)],SubTree)):- select([X,neg (A -> B)],Gamma,NewGamma),!, prove([[X,A]|[[X,neg B]|NewGamma]],Max,Cond,SubTree). prove(Gamma,Max,Cond,tree(orN,[X,neg (A or B)],SubTree)):- select([X,neg (A or B)],Gamma,NewGamma),!, prove([[X,neg A]|[[X,neg B]|NewGamma]],Max,Cond,SubTree). prove(Gamma,Max,Cond,tree(orP,[X,A or B],LeftTree,RightTree)):- select([X,A or B],Gamma,NewGamma),!, prove([[X,A]|NewGamma],Max,Cond,LeftTree), prove([[X,B]|NewGamma],Max,Cond,RightTree). prove(Gamma,Max,Cond,tree(andN,[X,neg (A and B)],LeftTree,RightTree)):- select([X,neg (A and B)],Gamma,NewGamma),!, prove([[X,neg A]|NewGamma],Max,Cond,LeftTree), prove([[X,neg B]|NewGamma],Max,Cond,RightTree). prove(Gamma,Max,Cond,tree(impP,[X,A -> B],LeftTree,RightTree)):- select([X,A -> B],Gamma,NewGamma),!, prove([[X,neg A]|NewGamma],Max,Cond,LeftTree), prove([[X,B]|NewGamma],Max,Cond,RightTree). prove(Gamma,Max,Cond,tree(<,[X,<,Y],Z,LeftTree,RightTree)):- member([X,<,Y],Gamma), Z in 1..Max, X#\=Z, Y#\=Z, \+member([X,<,Z],Gamma), \+member([Z,<,Y],Gamma), labeling([],[Y,Z]), gammaM(Gamma,Y,Z,ResLeft), gammaM(Gamma,Z,X,ResRight), append(ResLeft,Gamma,LeftConcl), append(ResRight,Gamma,RightConcl), prove([[Z,<,Y]|LeftConcl],Max,Cond,LeftTree), prove([[X,<,Z]|RightConcl],Max,Cond,RightTree). prove(Gamma,Max,Cond,tree(condN,[U,neg(A => B)],X,SubTree)):- select([U,neg (A => B)],Gamma,NewGamma),!, X#=Max+1, prove([[X,A]|[[X,box neg A]|[[X,neg B]|NewGamma]]],X,Cond,SubTree). prove(Gamma,Max,Cond,tree(boxN,[X,neg box neg A],Y,SubTree)):- select([X,neg box neg A],Gamma,NewGamma),!, Y#=Max+1, labeling([],[X]), gammaM(NewGamma,X,Y,GammaM), append(NewGamma,GammaM,GammaConclusion), prove([[Y,<,X]|[[Y,box neg A]|[[Y,A]|GammaConclusion]]],Y,Cond,SubTree). prove(Gamma,Max,Cond,tree(condP,[Y,A => B],LeftTree,RightTree)):- member([_,A => B],Gamma), \+member([A => B,_],Cond),!, Y in 1..Max, prove([[Y,A -> B]|Gamma],Max,[[A => B,[Y]]|Cond],LeftTree), prove([[Y,neg box neg A]|Gamma],Max,[[A => B,[Y]]|Cond],RightTree). prove(Gamma,Max,Cond,tree(condP,[Y,A => B],LeftTree,RightTree)):- member([_,A => B],Gamma), select([A => B,Used],Cond,NewCond), length(Used,N), N B]|Gamma],Max,[[A => B,[Y|Used]]|NewCond],LeftTree), prove([[Y,neg box neg A]|Gamma],Max,[[A => B,[Y|Used]]|NewCond],RightTree). /* Auxiliary predicate computing \Gamma^M_{x -> y} */ gammaM([],_,_,[]):-!. gammaM([[X,box A]|Tail],X,Y,[[Y,A]|[[Y,box A]|ResTail]]):-!,gammaM(Tail,X,Y,ResTail). gammaM([_|Tail],X,Y,ResTail):-gammaM(Tail,X,Y,ResTail). /* --------------------------------------------------------------------------------------- */ /* The following predicate builds a model for a satisfiable set of formulas: proveCounter(Gamma,[],[]) */ proveCounter(Gamma,_,_,_):-member([X,F],Gamma),member([X,neg F],Gamma),!. proveCounter(Gamma,_,_,_):-member([X,<,Y],Gamma),member([Y,<,X],Gamma),!. proveCounter(Gamma,Cond,Labels,Max):- select([X,neg neg A],Gamma,NewGamma),!, proveCounter([[X,A]|NewGamma],Cond,Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- select([X,A and B],Gamma,NewGamma),!, proveCounter([[X,A]|[[X,B]|NewGamma]],Cond,Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- select([X,neg (A -> B)],Gamma,NewGamma),!, proveCounter([[X,A]|[[X,neg B]|NewGamma]],Cond,Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- select([X,neg (A or B)],Gamma,NewGamma),!, proveCounter([[X,neg A]|[[X,neg B]|NewGamma]],Cond,Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- select([X,A or B],Gamma,NewGamma),!, proveCounter([[X,A]|NewGamma],Cond,Labels,Max),!, proveCounter([[X,B]|NewGamma],Cond,Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- select([X,neg (A and B)],Gamma,NewGamma),!, proveCounter([[X,neg A]|NewGamma],Cond,Labels,Max),!, proveCounter([[X,neg B]|NewGamma],Cond,Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- select([X,A -> B],Gamma,NewGamma),!, proveCounter([[X,neg A]|NewGamma],Cond,Labels,Max),!, proveCounter([[X,B]|NewGamma],Cond,Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- member([X,<,Y],Gamma), member(Z,Labels), X\=Z, Y\=Z, \+member([X,<,Z],Gamma), \+member([Z,<,Y],Gamma),!, gammaM(Gamma,Y,Z,ResLeft), gammaM(Gamma,Z,X,ResRight), append(ResLeft,Gamma,LeftConcl), append(ResRight,Gamma,RightConcl), proveCounter([[Z,<,Y]|LeftConcl],Cond,Labels,Max),!, proveCounter([[X,<,Z]|RightConcl],Cond,Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- select([_,neg (A => B)],Gamma,NewGamma),!, X is Max+1,!, proveCounter([[X,A]|[[X,box neg A]|[[X,neg B]|NewGamma]]],Cond,[X|Labels],X). proveCounter(Gamma,Cond,Labels,Max):- select([X,neg box neg A],Gamma,NewGamma),!, Y is Max+1,!, gammaM(NewGamma,X,Y,GammaM), append(NewGamma,GammaM,GammaConclusion), proveCounter([[Y,<,X]|[[Y,box neg A]|[[Y,A]|GammaConclusion]]],Cond,[Y|Labels],Y). proveCounter(Gamma,Cond,Labels,Max):- member([_,A => B],Gamma), \+member([A => B,_],Cond),!, member(U,Labels), proveCounter([[U,A -> B]|Gamma],[[A => B,[U]]|Cond],Labels,Max),!, proveCounter([[U,neg box neg A]|Gamma],[[A => B,[U]]|Cond],Labels,Max). proveCounter(Gamma,Cond,Labels,Max):- member([_,A => B],Gamma), select([A => B,Used],Cond,NewCond), member(U,Labels), \+member(U,Used),!, proveCounter([[U,A -> B]|Gamma],[[A => B,[U|Used]]|NewCond],Labels,Max),!, proveCounter([[U,neg box neg A]|Gamma],[[A => B,[U|Used]]|NewCond],Labels,Max). proveCounter(Gamma,_,Labels,_):- listOfAtoms(Gamma,Temp), listOfTransitions(Gamma,TempTrans), remove_duplicates(Temp,Atoms), transitivityOfRelation(TempTrans,OtherTransitions), append(TempTrans,OtherTransitions,Transitions), remove_duplicates(Gamma,Ext), throw(saturatedBranch(Atoms,Labels,Transitions,Ext)). /* Given a list of formulas, returns the list of atoms */ listOfAtoms([],[]):-!. listOfAtoms([[X,A]|Tail],[[X,A]|ResTail]):-atom(A),!,listOfAtoms(Tail,ResTail). listOfAtoms([_|Tail],ResTail):-!,listOfAtoms(Tail,ResTail). /* Given a list of formulas, returns the list of transitions (=values of the modular relation) */ listOfTransitions([],[]):-!. listOfTransitions([[X,<,Y]|Tail],[[X,<,Y]|ResTail]):-!,listOfTransitions(Tail,ResTail). listOfTransitions([_|Tail],ResTail):-!,listOfTransitions(Tail,ResTail). /* Transitive closure of the preference relation */ transitivityOfRelation(List,[]). /* Top-level predicates, also used to build a countermodel in case the initial set of formulas is satisfiable */ nmcr(K,F,Tree):-parseinput([F|K]),label(K,LabK), ((prove([[1,neg F]|LabK],1,[],Tree),!) ; catch(proveCounter([[1,neg F]|LabK],[],[1],1),saturatedBranch(Atoms,Max,Transitions,Gamma), canonicalModel(Atoms,Max,Transitions,Gamma,Tree)) ). canonicalModel(Atoms,Labels,Transitions,Gamma,model(Atoms,Labels,Transitions,Gamma)). unsatinterface(K,[F],Tree):-parseinput([F|K]),label(K,LabK),prove([[1,neg F]|LabK],1,[],Tree). unsatinterface(K,[],Tree):-parseinput(K),label(K,LabK),prove(LabK,1,[],Tree). countermodel(K,[F],Model):-parseinput([F|K]),label(K,LabK), catch(proveCounter([[1,neg F]|LabK],[],[1],1),saturatedBranch(Atoms,Labels,Transitions,Gamma), canonicalModel(Atoms,Labels,Transitions,Gamma,Model)). countermodel(K,[],Model):-parseinput(K),label(K,LabK), catch(proveCounter(LabK,[],[1],1),saturatedBranch(Atoms,Labels,Transitions,Gamma), canonicalModel(Atoms,Labels,Transitions,Gamma,Model)). /* Predicate used to control that the inserted formula belongs to the language LR of R logic: - propositional formulas belong to LR - if A and B are propositionals, then A => B belongs to LR - a boolean combination F of formulas of LR belongs to LR */ parseinput([]):-!. parseinput([F|Tail]):-parse(F),parseinput(Tail). parse(P):-atom(P). parse(F and G):-parse(F),parse(G). parse(F or G):-parse(F),parse(G). parse(F -> G):-parse(F),parse(G). parse(neg F):-parse(F). parse(A => B):-boolcomb(A),boolcomb(B). boolcomb(P):-atom(P). boolcomb(A and B):-boolcomb(A),boolcomb(B). boolcomb(A or B):-boolcomb(A),boolcomb(B). boolcomb(A -> B):-boolcomb(A),boolcomb(B). boolcomb(neg A):-boolcomb(A). /* Predicates used to reconstruct the proof tree or the model (used by the GUI) */ javaTree(tree(axiom),Node,jT(ax,Node,null,null)):-!. javaTree(tree(andP,[X,A and B],Sub),Node,jT(andP,Node,SubJava,null)):-!, select([X,A and B],Node,Temp), javaTree(Sub,[[X,A]|[[X,B]|Temp]],SubJava). javaTree(tree(andN,[X,neg (A and B)],S1,S2),Node,jT(andN,Node,SJ1,SJ2)):-!, select([X,neg (A and B)],Node,Temp), javaTree(S1,[[X,neg A]|Temp],SJ1), javaTree(S2,[[X,neg B]|Temp],SJ2). javaTree(tree(orP,[X,A or B],S1,S2),Node,jT(orP,Node,SJ1,SJ2)):-!, select([X,A or B],Node,Temp), javaTree(S1,[[X,A]|Temp],SJ1), javaTree(S2,[[X,B]|Temp],SJ2). javaTree(tree(orN,[X,neg (A or B)],Sub),Node,jT(orN,Node,SubJava,null)):-!, select([X,neg (A or B)],Node,Temp), javaTree(Sub,[[X,neg A]|[[X,neg B]|Temp]],SubJava). javaTree(tree(impP,[X,A -> B],S1,S2),Node,jT(impP,Node,SJ1,SJ2)):-!, select([X,A -> B],Node,Temp), javaTree(S1,[[X,neg A]|Temp],SJ1), javaTree(S2,[[X,B]|Temp],SJ2). javaTree(tree(neg,[X, neg neg A],Sub),Node,jT(neg,Node,SubJava,null)):-!, select([X,neg neg A],Node,Temp), javaTree(Sub,[[X,A]|Temp],SubJava). javaTree(tree(impN,[X,neg (A -> B)],Sub),Node,jT(impN,Node,SubJava,null)):-!, select([X,neg (A -> B)],Node,Temp), javaTree(Sub,[[X,A]|[[X,neg B]|Temp]],SubJava). javaTree(tree(condN,[U,neg (A => B)],X,Sub),Node,jT(entN,Node,SubJava,null)):-!, select([U,neg (A => B)],Node,Temp), javaTree(Sub,[[X,A]|[[X,box neg A]|[[X,neg B]|Temp]]],SubJava). javaTree(tree(boxN,[X,neg box neg A],Y,Sub),Node,jT(boxN,Node,SubJava,null)):-!, select([X,neg box neg A],Node,Temp), gammaM(Temp,X,Y,GammaM), append(Temp,GammaM,GammaConclusion), javaTree(Sub,[[Y,<,X]|[[Y,box neg A]|[[Y,A]|GammaConclusion]]],SubJava). javaTree(tree(<,[X,<,Y],Z,LeftTree,RightTree),Node,jT(<,Node,SJ1,SJ2)):-!, gammaM(Node,Y,Z,ResLeft), gammaM(Node,Z,X,ResRight), append(ResLeft,Node,LeftConcl), append(ResRight,Node,RightConcl), javaTree(LeftTree,[[Z,<,Y]|LeftConcl],SJ1), javaTree(RightTree,[[X,<,Z]|RightConcl],SJ2). javaTree(tree(condP,[U,A => B],S1,S2),Node,jT(entP,Node,SJ1,SJ2)):-!, javaTree(S1,[[U,A -> B]|Node],SJ1), javaTree(S2,[[U,neg box neg A]|Node],SJ2). /* Predicates used by the GUI to extract information about the closed tree */ getRule(jT(Rule,_,_,_),Rule):-!. getNode(jT(_,Node,_,_),Node):-!. getLeft(jT(_,_,Left,_),Left):-!. getRight(jT(_,_,_,Right),Right):-!. getAtoms(model(Atoms,_,_,_),Atoms):-!. getLabels(model(_,Labels,_,_),Labels):-!. getTransitions(model(_,_,Transitions,_),Transitions):-!. getExtended(model(_,_,_,Extended),Extended):-!. /* Given a list of formulas, returns the same list where formulas are labelled with 1 */ label([],[]):-!. label([Formula|Tail],[[1,Formula]|ResTail]):-label(Tail,ResTail). /* Given the number of labels, build the corresponding list of labels */ listOfLabels(1,[1]):-!. listOfLabels(N,[N|Tail]):-N1 is N-1,listOfLabels(N1,Tail).